package org.apache.lucene.index;

import java.io.IOException;

import org.apache.lucene.store.IndexInput;

public class TagSegmentTermEnum extends TagTermEnum {
  IndexInput input;
  TagFieldInfos tagFieldInfos;
  long size;
  long position = -1;

  private TagTermBuffer termBuffer = new TagTermBuffer();
  private TagTermBuffer prevBuffer = new TagTermBuffer();
  private TagTermBuffer scanBuffer = new TagTermBuffer(); // used for scanning

  private TagTermInfo termInfo = new TagTermInfo();

  private int format;
  private boolean isIndex = false;
  long indexPointer = 0;
  int indexInterval;
  int skipInterval;
  int maxSkipLevels;
  int prevNum;
  int scanNum;

  TagSegmentTermEnum(IndexInput i, TagFieldInfos tagFieldInfos, boolean isi) throws CorruptIndexException, IOException {
    input = i;
    this.tagFieldInfos = tagFieldInfos;
    isIndex = isi;
    maxSkipLevels = 1; // use single-level skip lists for formats > -3

    int firstInt = input.readInt();
    format = firstInt;

    // check that it is a format we can understand
    if (format < TermInfosWriter.FORMAT_CURRENT)
      throw new CorruptIndexException("Unknown format version:" + format + " expected " + TermInfosWriter.FORMAT_CURRENT + " or higher");

    size = input.readLong(); // read the size

    indexInterval = input.readInt();
    skipInterval = input.readInt();
    maxSkipLevels = input.readInt();
  }

  protected Object clone() {
    TagSegmentTermEnum clone = null;
    try {
      clone = (TagSegmentTermEnum) super.clone();
    } catch (CloneNotSupportedException e) {
    }
    clone.input = (IndexInput) input.clone();
    clone.termInfo = new TagTermInfo(termInfo);
    clone.termBuffer = (TagTermBuffer) termBuffer.clone();
    clone.prevBuffer = (TagTermBuffer) prevBuffer.clone();
    clone.scanBuffer = new TagTermBuffer();
    return clone;
  }

  final void seek(long pointer, int p, Term t, TagTermInfo ti) throws IOException {
    input.seek(pointer);
    position = p;
    termBuffer.set(t);
    prevBuffer.reset();
    termInfo.set(ti);
  }

  /** Increments the enumeration to the next element. True if one exists. */
  public boolean next() throws IOException {
    if (position++ >= size - 1) {
      prevBuffer.set(termBuffer);
      prevNum = termInfo.termNum;
      termBuffer.reset();
      return false;
    }
    prevBuffer.set(termBuffer);
    prevNum = termInfo.termNum;
    termBuffer.read(input, tagFieldInfos);
    termInfo.docFreq = input.readVInt(); 
    termInfo.termNum = input.readVInt();
    if (!isIndex) {
      long basePointer = input.getFilePointer();
      int blocksLength = input.readVInt();
      int infosLength = input.readVInt();
      termInfo.blockInfosPointer = basePointer + blocksLength;
      termInfo.basePointer = basePointer;
      input.seek(basePointer + blocksLength + infosLength);
    } else {
      indexPointer += input.readVLong(); 
    }
    return true;
  }
  
  final int scanTo(int termNum) throws IOException {
    scanNum = termNum;
    int count = 0;
    while (scanNum > termNum && next()) {
      count++;
    }
    return count;
  }
  
  /**
   * Optimized scan, without allocating new terms. Return number of invocations
   * to next().
   */
  final int scanTo(Term term) throws IOException {
    scanBuffer.set(term);
    int count = 0;
    while (scanBuffer.compareTo(termBuffer) > 0 && next()) {
      count++;
    }
    return count;
  }

  /**
   * Returns the current Term in the enumeration. Initially invalid, valid after
   * next() called for the first time.
   */
  public final Term term() {
    return termBuffer.toTerm();
  }
  
  public final int termNum() { 
    return termInfo.termNum;
  }
  
  final int prevNum() {
    return prevNum;
  }
  
  /** Returns the previous Term enumerated. Initially null. */
  final Term prev() {
    return prevBuffer.toTerm();
  }

  /**
   * Returns the current TermInfo in the enumeration. Initially invalid, valid
   * after next() called for the first time.
   */
  final TagTermInfo termInfo() {
    return new TagTermInfo(termInfo);
  }

  /**
   * Sets the argument to the current TermInfo in the enumeration. Initially
   * invalid, valid after next() called for the first time.
   */
  final void termInfo(TagTermInfo ti) {
    ti.set(termInfo);
  }

  /**
   * Returns the docFreq from the current TermInfo in the enumeration. Initially
   * invalid, valid after next() called for the first time.
   */
  public final int docFreq() {
    return termInfo.docFreq;
  }

  /*
   * Returns the blockInfosPointer from the current TermInfo in the enumeration.
   * Initially invalid, valid after next() called for the first time.
   */
  final long blockInfosPointer() {
    return termInfo.blockInfosPointer;
  }

  /** Closes the enumeration to further activity, freeing resources. */
  public final void close() throws IOException {
    input.close();
  }
}
